/*

 * Copyright (C) 2021 Huawei Device Co., Ltd.

 * Licensed under the Apache License, Version 2.0 (the "License");

 * you may not use this file except in compliance with the License.

 * You may obtain a copy of the License at

 *

 *     http://www.apache.org/licenses/LICENSE-2.0

 *

 * Unless required by applicable law or agreed to in writing, software

 * distributed under the License is distributed on an "AS IS" BASIS,

 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

 * See the License for the specific language governing permissions and

 * limitations under the License.

 */
package com.fxn.utils;

import com.fxn.interfaces.OnRecordStopListener;
import com.fxn.component.CameraView;
import ohos.agp.graphics.Surface;
import ohos.agp.graphics.SurfaceOps;
import ohos.agp.render.Paint;
import ohos.agp.text.Font;
import ohos.agp.utils.Color;
import ohos.agp.window.service.DisplayManager;
import ohos.app.Context;
import ohos.app.dispatcher.task.TaskPriority;
import ohos.eventhandler.EventHandler;
import ohos.eventhandler.EventRunner;
import ohos.global.icu.text.SimpleDateFormat;
import ohos.media.camera.CameraKit;
import ohos.media.camera.device.Camera;
import ohos.media.camera.device.CameraConfig;
import ohos.media.camera.device.CameraAbility;
import ohos.media.camera.device.FrameConfig;
import ohos.media.camera.device.CameraStateCallback;
import ohos.media.camera.params.Metadata;
import ohos.media.common.AudioProperty;
import ohos.media.common.Source;
import ohos.media.common.StorageProperty;
import ohos.media.common.VideoProperty;
import ohos.media.image.ImageReceiver;
import ohos.media.image.common.ImageFormat;
import ohos.media.image.common.Size;
import ohos.media.recorder.Recorder;

import java.io.File;
import java.io.IOException;
import java.util.Timer;
import java.util.TimerTask;
import java.util.Date;
import java.util.List;
import java.util.Calendar;
import java.util.ArrayList;

import static ohos.media.camera.device.Camera.FrameConfigType.FRAME_CONFIG_RECORD;

/**
 * description : CameraManager
 *
 * @since 2021-04-15
 */
public class CameraManager implements SurfaceOps.Callback {
    private Context mContext;
    private CameraView mCameraView;
    private boolean isRecording = false;
    private int recordTime;
    private Surface previewSurface;
    private Surface recorderSurface;
    private Camera cameraDevice;
    private CameraConfig.Builder cameraConfigBuilder;
    private ImageReceiver imageReceiver;
    private int flashMode = Metadata.FlashMode.FLASH_CLOSE;
    private Recorder recorder;
    private Timer timer;
    private ImageReceiver.IImageArrivalListener imageArrivalListener;
    private VideoProperty.Builder videoPropertyBuilder;
    private StorageProperty.Builder storagePropertyBuilder;
    private AudioProperty.Builder audioPropertyBuilder;
    private CameraAbility commCamera;
    private EventHandler eventHandler;
    private boolean isFrontCamera;
    private OnRecordStopListener mOnRecordStopListener;
    private Paint mCirclePaint;
    private TimerTask task =
            new TimerTask() {
                @Override
                public void run() {
                    mContext.getUITaskDispatcher()
                            .asyncDispatch(
                                    () -> mCameraView.invalidate());
                }
            };
    private Paint mRecordTimePaint;
    private String videoPath;
    private Source mSource;
    private String videoName;

    /**
     * 初始化 CameraManager
     *
     * @param context    context
     * @param cameraView cameraView
     */
    public CameraManager(Context context, CameraView cameraView) {
        mContext = context;
        mCameraView = cameraView;
        isFrontCamera = mCameraView.isFrontCamera();
        initRecordTimePaint();
        initVariable();
    }

    private void initRecordTimePaint() {
        mRecordTimePaint = new Paint();
        mRecordTimePaint.setColor(Color.WHITE);
        mRecordTimePaint.setTextSize(60);
        mRecordTimePaint.setStrokeWidth(3);
        mRecordTimePaint.setFont(new Font.Builder(CameraView.class.getName()).setWeight(500).build());
        mCirclePaint = new Paint();
        mCameraView.addDrawTask(
                (component, canvas) -> {
                    if (isRecording) {
                        if (recordTime == mCameraView.getMaxVideoDuration()) {
                            stopRecord();
                        }
                        recordTime++;
                        String text = new SimpleDateFormat("mm:ss").format(new Date(recordTime * 1000));
                        float textWidth = mRecordTimePaint.getTextBounds(text).getWidth();
                        float textHeight = mRecordTimePaint.getTextBounds(text).getHeight();
                        float circleCenter = (float) PixelUtil.screenWidth() / 2 - (textWidth + textHeight + 10) / 2;
                        canvas.drawCircle(circleCenter - textHeight / 2,
                                100 - (textHeight / 2),
                                textHeight / 2,
                                mCirclePaint,
                                Color.RED);
                        canvas.drawText(
                                mRecordTimePaint,
                                text,
                                circleCenter + (textHeight / 2) + 10,
                                100);
                    }
                });
    }

    @Override
    public void surfaceCreated(SurfaceOps surfaceOps) {
        LogUtil.loge("surfaceCreated is called :");
        surfaceOps.setFixedSize(mCameraView.getHeight(), mCameraView.getWidth());
        previewSurface = surfaceOps.getSurface();
        mContext.getUITaskDispatcher().asyncDispatch(() -> {
            if (!cameraInit()) {
                // 相机初始化失败
                ToastUtil.show(mContext, "相机初始化失败！");
                mContext.terminateAbility();
            }
            takePhotoInit();
        });
    }

    @Override
    public void surfaceChanged(SurfaceOps surfaceOps, int i1, int i2, int i3) {
    }

    @Override
    public void surfaceDestroyed(SurfaceOps surfaceOps) {
        // 释放相机设备
        if (cameraDevice != null) {
            // 关闭相机和释放资源
            cameraDevice.release();
        }
    }

    private boolean cameraInit() {
        isFrontCamera = mCameraView.isFrontCamera();
        CameraKit camerakit = CameraKit.getInstance(mContext.getApplicationContext());
        if (camerakit == null || camerakit.getCameraIds().length <= 0) {
            return false;
        }
        String cameraId = camerakit.getCameraIds()[0];
        if (camerakit.getCameraIds().length > 1) {
            cameraId = isFrontCamera ? camerakit.getCameraIds()[1] : camerakit.getCameraIds()[0];
        }
        commCamera = camerakit.getCameraAbility(cameraId);
        if (commCamera == null) {
            return false;
        }
        CameraStateCallback cameraStateCallback = new MyCameraStatusCallback();
        camerakit.createCamera(cameraId, cameraStateCallback, eventHandler);
        return true;
    }

    private void initVariable() {
        timer = new Timer();
        eventHandler = new EventHandler(EventRunner.current());
    }

    private void takePhotoInit() {
        // 获取拍照支持分辨率列表
        List<Size> pictureSizes = getSupportedSizes();
        if (!MyUtil.isListNoElement(pictureSizes)) {
            // 根据拍照要求选择合适的分辨率
            setPhotoSize(pictureSizes.get(0));
        }
    }

    /**
     * 设置录像结束的监听
     *
     * @param onRecordStopListener 录像结束的监听
     */
    public void setOnRecordStopListener(OnRecordStopListener onRecordStopListener) {
        mOnRecordStopListener = onRecordStopListener;
    }

    /**
     * 开始录像
     */
    public void startRecord() {
        cameraConfigBuilder = cameraDevice.getCameraConfigBuilder();
        if (cameraConfigBuilder == null) {
            LogUtil.loge("onCreated cameraConfigBuilder is null");
            return;
        }
        isRecording = true;
        mediaRecorderInit();
        recorderSurface = recorder.getVideoSurface();
        cameraConfigBuilder.addSurface(previewSurface);
        if (recorderSurface != null) {
            cameraConfigBuilder.addSurface(recorderSurface);
        }
        cameraDevice.configure(cameraConfigBuilder.build());
    }

    /**
     * 开始拍照
     */
    public void capture() {
        // 获取拍照配置模板
        FrameConfig.Builder pictureFrameConfigBuilder =
                cameraDevice.getFrameConfigBuilder(Camera.FrameConfigType.FRAME_CONFIG_PICTURE);
        // 配置拍照surface
        pictureFrameConfigBuilder.addSurface(imageReceiver.getRecevingSurface());
        pictureFrameConfigBuilder.setFlashMode(flashMode);
        pictureFrameConfigBuilder.setImageRotation(90);
        // 启动拍照
        cameraDevice.triggerSingleCapture(pictureFrameConfigBuilder.build());
    }

    private void mediaRecorderInit() {
        int rotation = DisplayManager.getInstance().getDefaultDisplay(mContext).get().getRotation();
        videoPropertyBuilder = new VideoProperty.Builder();
        storagePropertyBuilder = new StorageProperty.Builder();
        audioPropertyBuilder = new AudioProperty.Builder();
        // 设置录像方向
        videoPropertyBuilder.setRecorderDegrees(rotation);
        // 设置录制采样率
        videoPropertyBuilder.setRecorderFps(30);
        // 设置录像支持的分辨率，需保证width > height
        videoPropertyBuilder.setRecorderHeight(1080);
        videoPropertyBuilder.setRecorderWidth(1920);
        // 设置视频编码方式
        videoPropertyBuilder.setRecorderVideoEncoder(Recorder.VideoEncoder.DEFAULT);
        // 设置录制帧率
        videoPropertyBuilder.setRecorderRate(30);
        recorder = new Recorder();
        mSource = new Source();
        mSource.setRecorderAudioSource(Recorder.AudioSource.MIC);
        mSource.setRecorderVideoSource(Recorder.VideoSource.SURFACE);
        recorder.setSource(mSource);
        recorder.setOutputFormat(Recorder.OutputFormat.MPEG_4);
        videoName = Calendar.getInstance().getTimeInMillis() + ".mp4";
        String filePath = videoPath + videoName;
        File file = new File(filePath);
        storagePropertyBuilder.setRecorderFile(file);
        recorder.setStorageProperty(storagePropertyBuilder.build());
        audioPropertyBuilder.setRecorderAudioEncoder(Recorder.AudioEncoder.AAC);
        recorder.setAudioProperty(audioPropertyBuilder.build());
        recorder.setVideoProperty(videoPropertyBuilder.build());
        recorder.prepare();
    }

    /**
     * 停止录像
     */
    public void stopRecord() {
        recorder.stop();
        isRecording = false;
        task.cancel();
        recordTime = 0;
        mContext.getGlobalTaskDispatcher(TaskPriority.DEFAULT)
                .asyncDispatch(
                        () -> {
                                FileUtils.saveVideoToGallery(videoPath, videoName, mContext);
                            if (mOnRecordStopListener != null) {
                                mOnRecordStopListener.onRecordStop(videoPath + videoName);
                            }
                            });
        mContext.getUITaskDispatcher().asyncDispatch(() -> mCameraView.invalidate());
        if (cameraDevice == null) {
            LogUtil.loge("toStopRecord cameraDevice is null");
            return;
        }
        cameraConfigBuilder = cameraDevice.getCameraConfigBuilder();
        if (cameraConfigBuilder == null) {
            LogUtil.loge("toStopRecord cameraConfigBuilder is null");
            return;
        }
        cameraConfigBuilder.addSurface(previewSurface);
        // 配置拍照的Surface
        cameraConfigBuilder.addSurface(imageReceiver.getRecevingSurface());
        if (recorderSurface != null) {
            cameraConfigBuilder.removeSurface(recorderSurface);
            recorderSurface = null;
        }
        cameraDevice.configure(cameraConfigBuilder.build());
    }

    /**
     * 设置拍照监听
     *
     * @param imageArrivalListener 拍照监听
     */
    public void setImageArrivalListener(ImageReceiver.IImageArrivalListener imageArrivalListener) {
        this.imageArrivalListener = imageArrivalListener;
    }

    private void setPhotoSize(Size size) {
        // 创建ImageReceiver对象，注意creat函数中宽度要大于高度；5为最大支持的图像数，请根据实际设置
        imageReceiver =
                ImageReceiver.create(
                        Math.max(size.width, size.height), Math.min(size.width, size.height), ImageFormat.JPEG, 1);
        if (imageArrivalListener != null) {
            imageReceiver.setImageArrivalListener(imageArrivalListener);
        }
    }

    private List<Size> getSupportedSizes() {
        if (commCamera != null) {
            return commCamera.getSupportedSizes(SurfaceOps.class);
        } else {
            return new ArrayList<>();
        }
    }

    /**
     * 设置录像帧率
     *
     * @param fps 录像帧率
     */
    public void setRecordrFps(int fps) {
        videoPropertyBuilder.setRecorderFps(fps);
    }

    /**
     * 设置录像大小
     *
     * @param size 录像大小
     */
    public void setRecordrSize(Size size) {
        videoPropertyBuilder.setRecorderHeight(Math.min(size.height, size.width));
        videoPropertyBuilder.setRecorderWidth(Math.max(size.height, size.width));
    }

    /**
     * 设置录像格式
     *
     * @param format 录像格式
     */
    public void setRecordrOutputFormat(int format) {
        if (recorder != null) {
            recorder.setOutputFormat(format);
        }
    }

    /**
     * set CameraFlashMode
     *
     * @param flashMode CameraFlashMode
     */
    public void setCameraFlashMode(int flashMode) {
        this.flashMode = flashMode;
    }

    /**
     * 切换前/后置摄像头
     */
    public void switchCamera() {
        switchCamera(!isFrontCamera);
    }

    /**
     * 切换前/后置摄像头
     *
     * @param isFrontCamera 是否切换至前置摄像头；true：前置； false：后置
     */
    public void switchCamera(boolean isFrontCamera) {
        this.isFrontCamera = isFrontCamera;
        cameraInit();
    }

    /**
     * 是否正在录像
     *
     * @return true：正在录像； false：不在录像
     */
    public boolean isRecording() {
        return isRecording;
    }

    /**
     * 设置录像保存路径
     *
     * @param videoPath 录像保存路径
     */
    public void setVideoPath(String videoPath) {
        this.videoPath = videoPath;
    }

    private class MyCameraStatusCallback extends CameraStateCallback {
        @Override
        public void onCreated(Camera camera) {
            super.onCreated(camera);
            LogUtil.loge("camera created.");
            cameraDevice = camera;
            cameraConfigBuilder = camera.getCameraConfigBuilder();
            if (cameraConfigBuilder == null) {
                return;
            }
            CameraConfig cameraConfig =
                    cameraConfigBuilder
                            // 配置预览的Surface
                            .addSurface(previewSurface)
                            // 配置拍照的Surface
                            .addSurface(imageReceiver.getRecevingSurface())
                            .build();
            camera.configure(cameraConfig);
        }


        @Override
        public void onCreateFailed(String cameraId, int errorCode) {
            super.onCreateFailed(cameraId, errorCode);
            LogUtil.loge("camera create failed.errorCode：" + errorCode);
        }

        @Override
        public void onConfigured(Camera camera) {
            if (isRecording) {
                // 获取预览配置模板
                FrameConfig.Builder frameConfigBuilder =
                        camera.getFrameConfigBuilder(FRAME_CONFIG_RECORD);
                frameConfigBuilder.addSurface(previewSurface);
                frameConfigBuilder.setFlashMode(flashMode);
                if (recorderSurface != null) {
                    frameConfigBuilder.addSurface(recorderSurface);
                }
                // 通过 triggerLoopingCapture(FrameConfig)方法实现循环帧捕获(如预览/录像)。
                camera.triggerLoopingCapture(frameConfigBuilder.build());
                mContext.getGlobalTaskDispatcher(TaskPriority.DEFAULT)
                        .asyncDispatch(
                                () -> {
                                    recorder.start();
                                    try {
                                        timer.schedule(task, 1000, 1000);
                                    } catch (IllegalStateException e) {
                                        e.printStackTrace();
                                    }
                                });
            } else {
                // 获取预览配置模板
                FrameConfig.Builder frameConfigBuilder =
                        camera.getFrameConfigBuilder(Camera.FrameConfigType.FRAME_CONFIG_PREVIEW);
                frameConfigBuilder.addSurface(previewSurface);
                frameConfigBuilder.setFlashMode(flashMode);
                // 通过 triggerLoopingCapture(FrameConfig)方法实现循环帧捕获(如预览/录像)。
                camera.triggerLoopingCapture(frameConfigBuilder.build());
            }
        }

        @Override
        public void onConfigureFailed(Camera camera, int errorCode) {
            LogUtil.loge("camera configure failed.");
        }

        @Override
        public void onReleased(Camera camera) {
            LogUtil.loge("camera onReleased.");
            videoPropertyBuilder = null;
            storagePropertyBuilder = null;
            audioPropertyBuilder = null;
            if (recorder != null) {
                recorder.release();
            }
            if (isRecording) {
                stopRecord();
            }
        }

        @Override
        public void onFatalError(Camera camera, int errorCode) {
            LogUtil.loge("camera fatal error.");
            super.onFatalError(camera, errorCode);
        }
    }
}
